"use strict";
//import * as DateDemo    from './ES6/dateDemo.js'
//const DateDemo = require('./dateDemo.js')

// 深度优先遍历 filePath，如果是文件，打印尺寸和创建时间戳
function goThrough(filePath) {
    console.log('goThrough : ' + filePath);
    let fs = require("fs");

    let fileStat = fs.statSync(filePath);
    
    if (fileStat.isDirectory()) {
        let dirContents = fs.readdirSync(filePath);
        // console.dir(dirContents);
        for (var subPath of dirContents) {
            goThrough(filePath + '/' + subPath);
        }
    } else {
        console.log('    file: size=' + fileStat.size + ' ctimeMs=' + fileStat.ctimeMs);
    }

}

function txtFileWrite() {
    let fs = require("fs");
    let fd = fs.openSync('txtTest.txt', 'w');
    console.log("txtFileWrite, return by fs.openSync, fd=" + fd);

    let poem = `水调歌头·明月几时有
苏轼
丙辰中秋，欢饮达旦，大醉，作此篇，兼怀子由。
 
明月几时有？把酒问青天。
不知天上宫阙，今夕是何年。
我欲乘风归去，又恐琼楼玉宇，高处不胜寒。
起舞弄清影，何似在人间。
 
转朱阁，低绮户，照无眠。
不应有恨，何事长向别时圆？
人有悲欢离合，月有阴晴圆缺，此事古难全。
但愿人长久，千里共婵娟。
`;
    let writeRes = fs.writeSync(fd, poem, null, 'utf8');
    console.log("txtFileWrite, writeRes=" + writeRes);

    let closeRes = fs.closeSync(fd);
    console.log("txtFileWrite, closeRes=" + closeRes);

}

function txtFileRead() {
    // 一次性全部读取，不适用与大文件
    let fs = require("fs");
    let buf = fs.readFileSync("txtTest.txt");
    console.log('readFileSync ' + buf.length+'bytes');
    console.log(buf.toString());
}

function txtFileReadByBuffer() {
    // 开辟buffer 多次分段读取
    let fs = require("fs");
    let fd = fs.openSync('txtTest.txt', 'r');
    console.log("txtFileRead, return by fs.openSync, fd=" + fd);

    let buf = new Buffer.alloc(128);
    let readRes = 0;
    while (readRes = fs.readSync(fd, buf, 0, buf.length, null)) {
        console.log(readRes + "  字节被读取");
        if (readRes > 0) {
            console.log(buf.slice(0, readRes).toString());
        } else {
            break;
        }
    }

    let closeRes = fs.closeSync(fd);
    console.log("txtFileRead, closeRes=" + closeRes);
}

function txtFileReadAsyn() {
    // 开辟buffer 多次分段 异步读取
    let fs = require("fs");
    var buf = new Buffer.alloc(128);

    fs.open('txtTest.txt', 'r', function (err, fd) {
        if (err) {
            console.log('txtFileReadAsyn, open, err: '+err);
            return;
        }
        console.log("txtTest.txt, 读 打开成功！");
        var readPos = 0;
        
        let readFunc = function (err, bytes) {
            
            if (err) {
                console.log('txtFileReadAsyn, readFunc, err: '+err);
            }
            console.log(bytes + "  字节被读取");

            if (bytes > 0) {
                // 仅输出读取的字节
                console.log(buf.slice(0, bytes).toString());
                // 还没有读取完毕，继续 异步读取
                fs.read(fd, buf, 0, buf.length, null, readFunc);
            } else {
                // 读取完毕，关闭文件描述符
                let closeRes = fs.closeSync(fd);
                console.log("txtFileReadAsyn, closeRes=" + closeRes);
            }
        };

        fs.read(fd, buf, 0, buf.length, null, readFunc);
    });
}

class DemoPerson {
    // 操蛋只能有一个 constructor
    // constructor() {
    //     this.personName = '';
    //     this.gender = 'M';
    //     this.age = 0;
    //     this.height = 0
    //     this.addr = '';
    // }

    constructor(personName, gender, age, height, addr) {
        this.personName = personName;
        this.gender = gender;
        this.age = age;
        this.height = height
        this.addr = addr;
    }

    toString() {
        return `name:${this.personName}, gender:${this.gender}, age:${this.age}, height:${this.height} addr:${this.addr}`
    }

    readFromBinFile(fs, fd) {
        let readCount = 0;

        let buf4Int = Buffer.alloc(4);
        let buf4Doulbe = Buffer.alloc(8);
        let buf4Char = Buffer.alloc(1);

        // personName(字符串 长度 + 字符串bytes) 《-- 文件
        readCount += fs.readSync(fd, buf4Int);
        let strlen = buf4Int.readInt32LE();
        if (strlen >0) {
            let buf4Str = Buffer.alloc(strlen);
            readCount += fs.readSync(fd, buf4Str);
            this.personName = buf4Str.toString('utf8');
        }

        // gender 《-- 文件
        readCount += fs.readSync(fd, buf4Char);
        if ('M' == String.fromCharCode(buf4Char.readInt8())) {
            this.gender = 'M';
        } else {
            this.gender = 'F';
        }

        // age 《-- 文件
        readCount += fs.readSync(fd, buf4Int);
        this.age = buf4Int.readInt32LE();

        // height 《--文件
        readCount += fs.readSync(fd, buf4Doulbe);
        this.height = buf4Doulbe.readDoubleLE();

        // addr(字符串 长度 + 字符串bytes) 《-- 文件
        let buf4Int2 = Buffer.alloc(4);
        readCount += fs.readSync(fd, buf4Int2);
    
        strlen = buf4Int2.readInt32LE();
        if (strlen >0) {
            let buf4Str = Buffer.alloc(strlen);
            readCount += fs.readSync(fd, buf4Str);
            this.addr = buf4Str.toString('utf8');
        }
        
        console.log("readFromBinFile, readCount =" + readCount);
    }

    writeIntoBinFile(fs, fd) {
        let writeCount = 0;

        let buf4Int = Buffer.alloc(4);
        let buf4Doulbe = Buffer.alloc(8);
        let buf4Char = Buffer.alloc(1);

        // personName(字符串 长度 + 字符串bytes) --》文件
        let buf4Str = Buffer.from(this.personName, 'utf8')
        buf4Int.writeInt32LE(buf4Str.byteLength);
        writeCount += fs.writeSync(fd, buf4Int);
        writeCount += fs.writeSync(fd, buf4Str);

        // gender --》文件
        if ('M' == this.gender || 'm' == this.gender) {
            buf4Char.writeInt8('M'.charCodeAt(0));
        } else {
            buf4Char.writeInt8('F'.charCodeAt(0));
        }
        writeCount += fs.writeSync(fd, buf4Char);

        // age --》文件
        buf4Int.writeInt32LE(this.age);
        writeCount += fs.writeSync(fd, buf4Int);

        // height --》文件
        buf4Doulbe.writeDoubleLE(this.height);
        writeCount += fs.writeSync(fd, buf4Doulbe);

        // addr(字符串 长度 + 字符串bytes) --》文件
        buf4Str = Buffer.from(this.addr, 'utf8')
        buf4Int.writeInt32LE(buf4Str.byteLength);
        writeCount += fs.writeSync(fd, buf4Int);
        writeCount += fs.writeSync(fd, buf4Str);

        
        console.log("writeIntoFile, writeCount = " + writeCount);
    }
}

DemoPerson.defaultObj = () => {
    return new DemoPerson('', 'M', 0, 0, '');
}

function binFileWrite() {
    // 大端(Big-Endian)：   高位字节放内存低地址段；低位字节放内存高地址段
    // 小端(Little-Endian)：高位字节放内存高地址段；低位字节放内存低地址段
    // 0x12345678 大端内存存放： 低地址 --> | 12 | 34 | 56 | 78 | --> 高地址
    // 0x12345678 小端内存存放： 低地址 --> | 78 | 56 | 34 | 12 | --> 高地址
 
    //    Windos(x86,x64)和Linux(x86,x64)都是Little Endian操作系统。
    //    在ARM上，我见到的都是用Little Endian方式存储数据。
    //    C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的。
    //    JAVA编写的程序则唯一采用Big Endian方式来存储数据。
    //    所有网络协议也都是采用Big Endian的方式来传输数据的。所以有时我们也会把Big Endian方式称之为网络字节序。

    let p1 = new DemoPerson("1_LiLei_李雷",         'M', 12, 1.55, "505_class-1-1");
    let p2 = new DemoPerson("2_HanMeiMei_韩梅梅",   'F', 13, 1.61, "504_class-1-2");
    let p3 = new DemoPerson("3_JimGreen_吉姆",      'M', 11, 1.53, "503_class-1-3");

    let fs = require("fs");
    let fd = fs.openSync('binTest.bin', 'w');
    console.log("binFileWrite, return by fs.openSync, fd=" + fd);

    // 二进制文件开头写入 DemoPerson 实例的数量
    let buf = Buffer.alloc(4);
    buf.writeInt32LE(3);

    let writeBytes = fs.writeSync(fd, buf);
    console.log("binFileWrite, writeBytes =" + writeBytes);

    p1.writeIntoBinFile(fs, fd);
    p2.writeIntoBinFile(fs, fd);
    p3.writeIntoBinFile(fs, fd);
    fs.closeSync(fd);
}

function binFileRead() {
    let fs = require("fs");
    let fd = fs.openSync('binTest.bin', 'r');
    console.log("binFileRead, return by fs.openSync, fd=" + fd);

    let buf4Int = Buffer.alloc(4);
    let readRes = fs.readSync(fd, buf4Int);
    let instanceNum = buf4Int.readInt32LE();
    console.log("binFileRead, instanceNum = " + instanceNum);

    let personArr = [];
    for (let loop = 0; loop < instanceNum; ++loop) {
        let person = DemoPerson.defaultObj();
        person.readFromBinFile(fs, fd);
        personArr.push(person);
    }

    personArr.forEach(element => {
        console.log("from binfile : " + element);
    });

    fs.closeSync(fd);
}

function jsonFileWrite() {
    let fs = require("fs");
    let fd = fs.openSync('jsonTest.txt', 'w');
    console.log("jsonFileWrite, return by fs.openSync, fd=" + fd);

    let monthDic = {
        "January": 31,
        "February": 28,
        "March": 31,
        "April": 30,
        "May": 31,
        "June": 30,
        "July": 31,
        "August": 31,
        "September": 30,
        "October": 31,
        "November": 30,
        "December": 31,
    };
 
    let monthArr = Object.keys(monthDic);
 
    // js 8种数据类型 https://www.jb51.net/article/234040.htm
    //      在ES5的时候，我们认知的数据类型确实是 6种：Number、String、Boolean、undefined、object、Null
    //      ES6 中新增了一种 Symbol 。这种类型的对象永不相等，即始创建的时候传入相同的值，可以解决属性名冲突的问题，做为标记。
    //      谷歌67版本中还出现了一种 bigInt。是指安全存储、操作大整数。
    let jsObj = {
        'dic': monthDic,
        'arr': monthArr,
        'str': "stringTest",
        'num': 3.1416,
        'bool': true,
        'date': new Date(),
    };

    let writeRes = fs.writeSync(fd, JSON.stringify(jsObj), null, 'utf8');
    console.log("jsonFileWrite, writeRes=" + writeRes);

    let closeRes = fs.closeSync(fd);
    console.log("jsonFileWrite, closeRes=" + closeRes);
}

function jsonFileRead() {
    let fs = require("fs");
    let buf = fs.readFileSync("jsonTest.txt");
    console.log('jsonFileRead ' + buf.length+'bytes');
    let jsObj = JSON.parse(buf.toString());
    console.dir(jsObj);

    // 如果一个 Date 实例 被 JSON.stringify 变成字符串。 使用这个字符串构建 Date 实例 可以得到和原来值相同的 
    let dateFromJson = new Date(jsObj.date);
    console.log('dateFromJson = '+ dateFromJson.toLocaleString());
}

function demoLogic() {
    //console.log("\nfileDemoLogic");
    goThrough('.');

    txtFileWrite();

    txtFileRead();
    txtFileReadByBuffer();
    //txtFileReadAsyn();

    binFileWrite();
    binFileRead();

    jsonFileWrite();
    jsonFileRead();
}

//export { demoLogic };

module.exports.demoLogic = demoLogic;